﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using StockFinder.Services.Extractors;
using StockFinder.Services.Extractors.Yahoo;
using StockFinder.Model;
using log4net;
using Db4objects.Db4o;
using StockFinder.Model.Constants;
using Db4objects.Db4o.Config;
using System.Threading.Tasks;
using System.Net;
using System.IO;

namespace StockFinder.Services.Engines
{
    public class PriceHistoryDatabaseUpdater
    {
        private static readonly ILog _Log = LogManager.GetLogger(typeof(PriceHistoryDatabaseUpdater));   

        private IHistoricPricesExtractor _YahooHistoricPricesExtractor;

        public PriceHistoryDatabaseUpdater()
        {
            _YahooHistoricPricesExtractor = new YahooHistoricPriceExtractor();
        }

        private PriceHistoryDatabase _PriceHistoryDatabase = new PriceHistoryDatabase();

        public PriceHistoryDatabase PriceHistoryDatabase
        {
            get { return _PriceHistoryDatabase; }
        }


        /// <summary>
        /// Attempts to get the price history saved to file
        /// </summary>
        public void LoadFromFile()
        {
            string databaseFileName = ApplicationConfiguation.ObjectDatabaseSRootDirectory + ObjectDatabaseFilenames.PRICE_HISTORY_DATABASE;

            // save the price database
            using (IObjectContainer database = Db4oEmbedded.OpenFile(GeCreateIEmbeddedConfiguration(),
                ApplicationConfiguation.ObjectDatabaseSRootDirectory + ObjectDatabaseFilenames.PRICE_HISTORY_DATABASE))
            {
                var priceHistoryDatabases = database.Query<PriceHistoryDatabase>();

                if (priceHistoryDatabases == null || priceHistoryDatabases.Count == 0)
                {
                    throw new Exception(string.Format("Could not load from file location {0}", databaseFileName));
                }

                _PriceHistoryDatabase = priceHistoryDatabases[0];

                database.Activate(_PriceHistoryDatabase, 2);
                database.Activate(_PriceHistoryDatabase.SymbolPriceHistoryItems, 5);
            }
        }

        public void Run(List<IBDRatings> ibdRatings)
        {                               
            const int YEARS_BACK_TO_DOWNLOAD = -30;

            List<IBDRatings> badSymbols = new List<IBDRatings>();
            List<IBDRatings> reallyBadSymbols = new List<IBDRatings>();

            //generate filename
            string databaseFileName = ApplicationConfiguation.ObjectDatabaseSRootDirectory + ObjectDatabaseFilenames.PRICE_HISTORY_DATABASE;

            //remove if already exists, want latest only
            if (File.Exists(databaseFileName))
            {
                File.Delete(databaseFileName);
            }

            _Log.DebugFormat("Downloading {0} years of price history for {1} symbols", YEARS_BACK_TO_DOWNLOAD, ibdRatings.Count);

            // save the price database
            using (IObjectContainer database = Db4oEmbedded.OpenFile(GeCreateIEmbeddedConfiguration(),
                ApplicationConfiguation.ObjectDatabaseSRootDirectory + ObjectDatabaseFilenames.PRICE_HISTORY_DATABASE))
            {
                PriceHistoryDatabase priceHistoryDatabase = new PriceHistoryDatabase();
                database.Store(priceHistoryDatabase);
            }

            // save the price database
            using (IObjectContainer database = Db4oEmbedded.OpenFile(GeCreateIEmbeddedConfiguration(),
                ApplicationConfiguation.ObjectDatabaseSRootDirectory + ObjectDatabaseFilenames.PRICE_HISTORY_DATABASE))
            {
                PriceHistoryDatabase priceHistoryDatabase = null;

                var priceHistoryDatabases = database.Query<PriceHistoryDatabase>();

                if (priceHistoryDatabases == null || priceHistoryDatabases.Count == 0)
                {
                    throw new Exception(string.Format("Could not load from file location {0}", databaseFileName));
                }

                priceHistoryDatabase = priceHistoryDatabases[0];

                //activate the price items into memory
                database.Activate(priceHistoryDatabase.SymbolPriceHistoryItems, 2);

                //generate list
                foreach (var item in ibdRatings)
                {
                    priceHistoryDatabase.SymbolPriceHistoryItems.Add(new SymbolPriceHistoryItem()
                    {
                        IBDRatings = item,
                        Symbol = item.Symbol
                    });
                }

                foreach (var item in priceHistoryDatabase.SymbolPriceHistoryItems)
                {
                    for (int i = 0; i < 10000; i++)
                    {
                        item.DailyPrices.Add(new DailyPrice());
                    }
                }

                //store the symbol price history items
                database.Store(priceHistoryDatabase.SymbolPriceHistoryItems);
            }

            // save the price database
            using (IObjectContainer database = Db4oEmbedded.OpenFile(GeCreateIEmbeddedConfiguration(),
                ApplicationConfiguation.ObjectDatabaseSRootDirectory + ObjectDatabaseFilenames.PRICE_HISTORY_DATABASE))
            {
                PriceHistoryDatabase priceHistoryDatabase = null;

                var priceHistoryDatabases = database.Query<PriceHistoryDatabase>();

                if (priceHistoryDatabases == null || priceHistoryDatabases.Count == 0)
                {
                    throw new Exception(string.Format("Could not load from file location {0}", databaseFileName));
                }

                priceHistoryDatabase = priceHistoryDatabases[0];

                //activate the price items into memory
                database.Activate(priceHistoryDatabase.SymbolPriceHistoryItems, 2);

                SymbolPriceHistoryItem priceHistoryItem = null;
                List<DailyPrice> prices = null;

                for (int i = 0; i < priceHistoryDatabase.SymbolPriceHistoryItems.Count; i++)
                {
                    priceHistoryItem = priceHistoryDatabase.SymbolPriceHistoryItems[i];

                    _Log.InfoFormat("Updating {0} price history", priceHistoryItem.Symbol);

                    //now activate symbols prices list
                    database.Activate(priceHistoryItem.DailyPrices, 2);

                    prices = _YahooHistoricPricesExtractor.GetHistoricPrices(
                        DateTime.Now.AddYears(YEARS_BACK_TO_DOWNLOAD), DateTime.Today, priceHistoryItem.Symbol);

                    if (prices != null && prices.Count > 0)
                    {
                        priceHistoryItem.DailyPrices.AddRange(prices);
                        database.Store(priceHistoryItem.DailyPrices);                    
                    }
                    else
                    {

                    }

                    //clean up references otherwise flooded with prices                  
                    priceHistoryItem.DailyPrices.Clear();
                    priceHistoryItem.DailyPrices = null;
                    database.Deactivate(priceHistoryItem, 5);
                    GC.Collect();

                    _Log.DebugFormat("{0:0.0%} completed", (double)i / ibdRatings.Count);
                }                

                #region Bad symbols
                /*
                _Log.DebugFormat("Trying bad symbols again");

                string formattedSymbol = null;

                foreach (var item in badSymbols)
                {
                    _Log.DebugFormat("Symbol: {0}", item);

                    //1. first try ABVC -> ABV-C
                    formattedSymbol = string.Format("{0}-{1}", item.Symbol.Substring(0, item.Symbol.Length - 1), item.Symbol[item.Symbol.Length - 1]);

                    _Log.DebugFormat("Symbol attempt 1: {0}", formattedSymbol);

                    if (_YahooHistoricPricesExtractor.LoadHistoricPrices(
                        DateTime.Now.AddYears(YEARS_BACK_TO_DOWNLOAD), DateTime.Today, formattedSymbol, dailyPriceTransporter))
                    {
                        priceHistoryItem = new SymbolPriceHistoryItem();
                        priceHistoryItem.Symbol = item.Symbol;
                        priceHistoryItem.IBDRatings = item;

                        if (priceHistoryItem.DailyPrices == null) priceHistoryItem.DailyPrices = new List<DailyPrice>();
                        priceHistoryItem.DailyPrices.AddRange(dailyPriceTransporter.Where(x => x.IsSet));

                        priceHistoryDatabase.SymbolPriceHistoryItems.Add(priceHistoryItem);
                    }
                    else
                    {
                        //2. then try ABVC -> ABV-PC
                        formattedSymbol = string.Format("{0}-P{1}", item.Symbol.Substring(0, item.Symbol.Length - 1), item.Symbol[item.Symbol.Length - 1]);

                        _Log.DebugFormat("Symbol attempt 2: {0}", formattedSymbol);

                        if (_YahooHistoricPricesExtractor.LoadHistoricPrices(
                            DateTime.Now.AddYears(YEARS_BACK_TO_DOWNLOAD), DateTime.Today, formattedSymbol, dailyPriceTransporter))
                        {
                            priceHistoryItem = new SymbolPriceHistoryItem();
                            priceHistoryItem.Symbol = item.Symbol;
                            priceHistoryItem.IBDRatings = item;

                            if (priceHistoryItem.DailyPrices == null) priceHistoryItem.DailyPrices = new List<DailyPrice>();
                            priceHistoryItem.DailyPrices.AddRange(dailyPriceTransporter.Where(x => x.IsSet));

                            priceHistoryDatabase.SymbolPriceHistoryItems.Add(priceHistoryItem);
                        }
                        else
                        {
                            reallyBadSymbols.Add(item);
                            _Log.WarnFormat("Couldnt download prices even after formatting");
                        }
                    }
                }

                _Log.DebugFormat("Couldnt download these even after reformatting...");

                reallyBadSymbols.ForEach(i => _Log.DebugFormat("{0}", i));
                 * */
                #endregion
            }
        }

        private IEmbeddedConfiguration GeCreateIEmbeddedConfiguration()
        {
            IEmbeddedConfiguration config = Db4oEmbedded.NewConfiguration();
            config.Common.ActivationDepth = 1;
            config.File.BlockSize = 8;            
            //config.Common.ObjectClass(typeof(PriceHistoryContainer)).CascadeOnUpdate(true);
            //config.Common.ObjectClass(typeof(DailyPrice)).CascadeOnUpdate(true);
            //config.Common.ObjectClass(typeof(Symbol)).CascadeOnUpdate(true);

            return config;
        }
    }
}
